package com.da.landlord;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.annotation.Resource;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.googlecode.cswish.oosearch.QLInfo;
import com.googlecode.cswish.struts.spring.GenericService;
import com.googlecode.cswish.struts.spring.ObjectFactory;
import com.opensymphony.xwork2.inject.Inject;
import com.ra.landlord.FeeAdditionRecord;
import com.ra.landlord.FeeRecord;
import com.ra.landlord.FeeRuleCfg;
import com.ra.landlord.constant.LandlordConstant;
import com.ra.order.rental.RentalOrder;
import com.ra.order.rental.RentalOrderService;
import com.ra.util.DateTimeUtil;

@Service
public class DaChargeBillRecordService {

	@PersistenceContext(unitName="espirit")
	protected EntityManager em;
	
	private static final Log logger = LogFactory.getLog(DaChargeBillRecordService.class);
	
	@Inject
	JpaTransactionManager txManager;
	
	@Resource
	private GenericService genericService;
	
	@Resource
	private DaChargeItemService chargeItemService;
	
	@Resource
	private DaRoomService roomService;
	
	@Resource
	private RentalOrderService rentalOrderService;
	
	private DaChargeBillRecordService chargeBillRecordService;
	
	public DaChargeBillRecordService getThis() {
		if (chargeBillRecordService == null) {
			chargeBillRecordService = ObjectFactory.getInstance().getBean(DaChargeBillRecordService.class, false);
		}
		return chargeBillRecordService;
	}
	
	/**
	 * 修复没有chargebillrecord的待收chargeItem
	 * @return
	 */
	public boolean repaireChargeBillRecord() {
		QLInfo qlInfo = new QLInfo("select a from " + DaRoom.class.getName() + " a where a.agreement is not null and a.rentState = ? and exists (from a.chargeItemsWithNoConditon b where b.status != ? and b.chargeBillRecord is null)", true);
		qlInfo.addParam(DaRoom.RENT_STATE_RENT);
		qlInfo.addParam(DaChargeItem.STATUS_PAID);
		int count = genericService.getCount(qlInfo, false);
		
		for (int i = 0; i < count; i+= 1000) {
			qlInfo.setFirstResult(0);
			qlInfo.setMaxResults(1000);
			List<DaRoom> rooms = genericService.searchByQl(qlInfo).getData();
			if (rooms != null && rooms.size() > 0) {
				getThis().repaireChargeBillRecordInterval(rooms);
			}
		}
		return true;
	}
	
	@Transactional
	public void repaireChargeBillRecordInterval(List<DaRoom> rooms) {
		for (DaRoom room : rooms) {
			QLInfo qlInfo2 = new QLInfo("select a from " + DaChargeItem.class.getName() + " a where a.status != ? and a.room = ?", false);
			qlInfo2.addParam(DaChargeItem.STATUS_PAID);
			qlInfo2.addParam(room);
			List<DaChargeItem> chargeItems = genericService.searchByQl(qlInfo2).getData();
			
			DaChargeBillRecord chargeBillRecord = new DaChargeBillRecord();
			chargeBillRecord.setAgreement(room.getAgreement());
			chargeBillRecord.setRoom(room);
			chargeBillRecord.setCreateTime(new Date());
			List<DaChargeItem> billChargeItems = new ArrayList<DaChargeItem>(chargeItems.size());
			BigDecimal amountRecv = BigDecimal.ZERO;
			BigDecimal amountActual = BigDecimal.ZERO;
			
			for (DaChargeItem dbChargeItem : chargeItems) {
				if (DaChargeItem.CHARGE_TYPE_RENTAL.equals(dbChargeItem.getChargeType())) {
					chargeBillRecord.setDateBeginRecv(dbChargeItem.getDateBeginRecv());
					chargeBillRecord.setDateEndRecv(dbChargeItem.getDateEndRecv());
				}
				amountRecv = amountRecv.add(dbChargeItem.getPriceRecv());
				if (dbChargeItem.getPriceActual() != null) {
					amountActual = amountActual.add(dbChargeItem.getPriceActual());
				} else {
					amountActual = amountActual.add(dbChargeItem.getPriceRecv());
				}
				billChargeItems.add(dbChargeItem);
			}
			if (chargeBillRecord.getDateBeginRecv() == null || chargeBillRecord.getDateEndRecv() == null) {
				continue;
			}
			chargeBillRecord.setAmountActual(amountActual);
			chargeBillRecord.setAmountRecv(amountRecv);
			chargeBillRecord.setBalance(amountActual.subtract(amountRecv));
			chargeBillRecord.setLandlordUser(room.getLandlordUser());
			chargeBillRecord.setChargeItems(billChargeItems);
			em.persist(chargeBillRecord);
		}
		em.flush();;
		em.clear();
	}
	
	/**
	 * 修复rentalOrder里面额chargebillrecord字段
	 * @return
	 */
	public boolean repairRentalOrderChargeBillRecord() {
		QLInfo qlInfo = new QLInfo("select a from " + RentalOrder.class.getName() + " a where a.chargeBillRecord is null and a.roomType = ?", true);
		qlInfo.addParam(LandlordConstant.ROOMTYPE_DA);
		int count = genericService.getCount(qlInfo, false);
		
		for (int i = 0; i < count; i+= 1000) {
			qlInfo.setFirstResult(0);
			qlInfo.setMaxResults(1000);
			List<RentalOrder> rentalOrders = genericService.searchByQl(qlInfo).getData();
			if (rentalOrders != null && rentalOrders.size() > 0) {
				getThis().repairRentalOrderChargeBillRecordInterval(rentalOrders);
			}
		}
		return true;
	}
	
	@Transactional
	public void repairRentalOrderChargeBillRecordInterval(List<RentalOrder> rentalOrders) {
		for (RentalOrder rentalOrder : rentalOrders) {
			try {
				List<DaChargeItem> chargeItems = rentalOrder.getChargeItems();
				DaChargeBillRecord chargeBillRecord = new DaChargeBillRecord();
				if (RentalOrder.ORDER_STATE_SUCCESS == rentalOrder.getOrderState()) {
					chargeBillRecord.setAgreement(rentalOrder.getChargeItems().get(0).getChargeBill().getAgreement());
				} else if (RentalOrder.ORDER_STATE_CLOSED != rentalOrder.getOrderState()){
					chargeBillRecord.setAgreement(rentalOrder.getDaRoom().getAgreement());
				}
				
				chargeBillRecord.setRoom(rentalOrder.getDaRoom());
				chargeBillRecord.setCreateTime(new Date());
				List<DaChargeItem> billChargeItems = new ArrayList<DaChargeItem>(chargeItems.size());
				BigDecimal amountRecv = BigDecimal.ZERO;
				BigDecimal amountActual = BigDecimal.ZERO;
				
				for (DaChargeItem dbChargeItem : chargeItems) {
					if (DaChargeItem.CHARGE_TYPE_RENTAL.equals(dbChargeItem.getChargeType())) {
						chargeBillRecord.setDateBeginRecv(dbChargeItem.getDateBeginRecv());
						chargeBillRecord.setDateEndRecv(dbChargeItem.getDateEndRecv());
					}
					amountRecv = amountRecv.add(dbChargeItem.getPriceRecv());
					if (dbChargeItem.getPriceActual() != null) {
						amountActual = amountActual.add(dbChargeItem.getPriceActual());
					} else {
						amountActual = amountActual.add(dbChargeItem.getPriceRecv());
					}
					billChargeItems.add(dbChargeItem);
				}
				
				if (chargeBillRecord.getDateBeginRecv() == null || chargeBillRecord.getDateEndRecv() == null) {
					continue;
				}
				
				chargeBillRecord.setAmountActual(amountActual);
				chargeBillRecord.setAmountRecv(amountRecv);
				chargeBillRecord.setBalance(amountActual.subtract(amountRecv));
				chargeBillRecord.setLandlordUser(rentalOrder.getDaRoom().getLandlordUser());
				chargeBillRecord.setChargeItems(billChargeItems);
				em.persist(chargeBillRecord);
				rentalOrder.setChargeBillRecord(chargeBillRecord);
			} catch (Exception e) {
				logger.error("修复rentalOrder的chargeBillRecord失败，rentalOrderId:" + rentalOrder.getId(), e);
			}
		}
		em.flush();;
		em.clear();
	}
	
	/**
	 * 执行生成待收的dachargetbill后，应当调用此接口修复chargeBill的已收/未收状态。
	 * @return
	 */
	public boolean repairRentalOrderChargeBillRecordPayState() {
		QLInfo qlInfo = new QLInfo("select a from " + DaChargeBillRecord.class.getName() + " a where a.status is null", true);
		int count = genericService.getCount(qlInfo, false);

		for (int i = 0; i < count; i+= 1000) {
			qlInfo.setFirstResult(0);
			qlInfo.setMaxResults(1000);
			List<DaChargeBillRecord> chargeBillRecords = genericService.searchByQl(qlInfo).getData();
			if (chargeBillRecords != null && chargeBillRecords.size() > 0) {
				getThis().repairRentalOrderChargeBillRecordPayStateInterval(chargeBillRecords);
			}
		}
		return true;
	}
	
	@Transactional
	public void repairRentalOrderChargeBillRecordPayStateInterval(List<DaChargeBillRecord> chargeBillRecords) {
		for (DaChargeBillRecord chargeBillRecord : chargeBillRecords) {
			boolean hasPay = false;
			for (DaChargeItem chargeItem : chargeBillRecord.getChargeItems()) {
				if (DaChargeItem.CHARGE_TYPE_RENTAL.equals(chargeItem.getChargeType()) && DaChargeItem.STATUS_PAID.equals(chargeItem.getStatus())) {
					hasPay = true;
					break;
				}
			}
			if (hasPay) {
				chargeBillRecord.setStatus(DaChargeBillRecord.STATUS_RENT_DONE);
			} else {
				chargeBillRecord.setStatus(DaChargeBillRecord.STATUS_RENT_TODO);
			}
		}
		em.flush();
		em.clear();
	}
	
	@Transactional
	public void updateChargeBillRecordFromAgreement(DaAgreement dbAgreement, BigDecimal oldDeposit) {
		DaChargeBillRecord dbChargeBillRecord = chargeItemService.getOrGenerateChargeBillRecord(dbAgreement.getRoom());
		if (dbChargeBillRecord == null) {
			return ;
		}
		
		if (oldDeposit.compareTo(dbAgreement.getDeposit()) !=0 && dbAgreement.getChargeBills()!= null && dbAgreement.getChargeBills().size() != 0) {
			//新出租的房间，要产生押金
			DaChargeItem chargeItem = new DaChargeItem();
			chargeItem.setName("押金（补差）");
			chargeItem.setDateBeginRecv(dbChargeBillRecord.getDateBeginRecv());
			chargeItem.setDateEndRecv(dbChargeBillRecord.getDateEndRecv());
			chargeItem.setRoom(dbAgreement.getRoom());
			chargeItem.setFeeRuleCfg(null);
			chargeItem.setPriceRecv(dbAgreement.getDeposit().subtract(oldDeposit).setScale(2, RoundingMode.HALF_UP));
			chargeItem.setStatus(DaChargeItem.STATUS_CONFIRMED);
			chargeItem.setChargeType(DaChargeItem.CHARGE_TYPE_CUSTOM);
			chargeItem.setCreateChargeTime(new Date());
			dbChargeBillRecord.getChargeItems().add(chargeItem);
			dbChargeBillRecord.setAmountRecv(dbChargeBillRecord.getAmountRecv().add(chargeItem.getPriceRecv()));
		} else {
			List<DaChargeItem> chargeItems = dbChargeBillRecord.getChargeItems();			
			for (DaChargeItem chargeItem : chargeItems) {
				if (DaChargeItem.CHARGE_TYPE_DEPOSIT.equals(chargeItem.getChargeType())) {
					chargeItem.setPriceRecv(dbAgreement.getDeposit());
					break;
				}
			}
		}
		
		RentalOrder rentalOrder = roomService.getRentalOrder(dbChargeBillRecord);
		if (rentalOrder != null) {
			return ;	//已经生成网上收租单，不需要修复时间和房租。
		}
		
		if (dbAgreement.getChargeBills() == null || dbAgreement.getChargeBills().size() == 0) {
			dbChargeBillRecord.setDateBeginRecv(dbAgreement.getRentBeginDate());
			dbChargeBillRecord.setDateEndRecv(DateTimeUtil.getFeeEndDate(dbChargeBillRecord.getDateBeginRecv(), dbAgreement.getPayNum()));
			
			BigDecimal newRental = dbAgreement.getRentPay().multiply(BigDecimal.valueOf(Double.valueOf(dbAgreement.getPayNum())));
			DaChargeItem rentalChargeItem = dbChargeBillRecord.getChargeItems().get(0);
			dbChargeBillRecord.setAmountRecv(dbChargeBillRecord.getAmountRecv().add(newRental.subtract(rentalChargeItem.getPriceRecv()).setScale(2, RoundingMode.HALF_UP)));
			rentalChargeItem.setPriceRecv(newRental);
		} else {
			dbChargeBillRecord.setDateEndRecv(DateTimeUtil.getFeeEndDate(dbChargeBillRecord.getDateBeginRecv(), dbAgreement.getRentCycle() + 1));
			
			BigDecimal newRental = dbAgreement.getRentPay().multiply(BigDecimal.valueOf(Double.valueOf(dbAgreement.getRentCycle() + 1)));
			DaChargeItem rentalChargeItem = dbChargeBillRecord.getChargeItems().get(0);
			dbChargeBillRecord.setAmountRecv(dbChargeBillRecord.getAmountRecv().add(newRental.subtract(rentalChargeItem.getPriceRecv()).setScale(2, RoundingMode.HALF_UP)));
			rentalChargeItem.setPriceRecv(newRental);
		}
		chargeItemService.repaireFixedChargeItem(dbAgreement.getRoom().getSuite());
	}
	
	/**
	 * 将dachargeItem的feeRule填充到feeRuleCfg字段，然后废弃feerule。 
	 * @return
	 */
	public boolean repaireFeeRuleCfg() {
		QLInfo qlInfo = new QLInfo("select a from " + DaChargeItem.class.getName() + " a where a.feeRuleCfg is null and a.feeRule is not null", true);
		int count = genericService.getCount(qlInfo, false);

		for (int i = 0; i < count; i+= 1000) {
			qlInfo.setFirstResult(0);
			qlInfo.setMaxResults(1000);
			List<DaChargeItem> chargeItems = genericService.searchByQl(qlInfo).getData();
			if (chargeItems != null && chargeItems.size() > 0) {
				getThis().repaireFeeRuleCfgInterval(chargeItems);
			}
		}
		return true;
	}
	
	@Transactional
	public void repaireFeeRuleCfgInterval(List<DaChargeItem> chargeItems) {
		for (DaChargeItem chargeItem : chargeItems) {
			try{
				chargeItem.setFeeRuleCfg(chargeItem.getFeeRule().getFeeRuleCfg());
			} catch(Exception e) {
				logger.error("修复ChargeItem的feeRuleCfg字段失败，chargeItemId:" + chargeItem.getId());
			}
		}
		em.flush();
		em.clear();
		logger.info("修复ChargeItem的feeRuleCfg字段成功，1000条，最新的一条chargeItemId:"  + chargeItems.get(chargeItems.size() - 1).getId());
	}
	
	@SuppressWarnings("unchecked")
	@Transactional
	public boolean repaireChargeBillRecordWhenFeeRuleCfgPriceChanged(FeeRuleCfg feeRuleCfg, BigDecimal newPrice) {
		FeeRuleCfg dbFeeRuleCfg = genericService.load(feeRuleCfg);
		BigDecimal oldPrice = dbFeeRuleCfg.getFeeRuleCfgCalc().getPrice();
		QLInfo qlInfo = new QLInfo("select a from " + DaChargeItem.class.getName() + " a where a.feeRuleCfg = ? and a.status != ?", false, dbFeeRuleCfg, DaChargeItem.STATUS_PAID);
		List<DaChargeItem> dbChareItems = genericService.searchByQl(qlInfo).getData();
		for (DaChargeItem chargeItem : dbChareItems) {
			if (chargeItem.getStatus().equals(DaChargeItem.STATUS_NEED_CONFIRM)) {
				chargeItem.setPriceRecv(chargeItem.getPriceRecv().multiply(newPrice).divide(oldPrice, 2, RoundingMode.HALF_UP));
				em.merge(chargeItem);
			} else {
				DaChargeBillRecord chargeBillRecord = chargeItem.getChargeBillRecord();
				RentalOrder dbRentalOrder = rentalOrderService.searchRentalOrderByChargeBillRecord(chargeBillRecord);
				if (dbRentalOrder == null) {
					BigDecimal newTotalPrice = chargeItem.getPriceRecv().multiply(newPrice).divide(oldPrice, 2, RoundingMode.HALF_UP);
					BigDecimal needAddPrice = newTotalPrice.subtract(chargeItem.getPriceRecv());
					chargeItem.setPriceRecv(newTotalPrice);
					chargeBillRecord.setAmountRecv(chargeBillRecord.getAmountRecv().add(needAddPrice));
					em.merge(chargeBillRecord);
				} else {
					chargeItem.setPriceRecv(chargeItem.getPriceRecv().multiply(newPrice).divide(oldPrice, 2, RoundingMode.HALF_UP));
					em.merge(chargeItem);
				}
			}
		}
		return true;
	}
}
